//========================================================================================
// MerHashMap.cc
// ----------------------------------------------------------------------------
// $Id: //CBT-depot/CBT++/src/MerHashMap.cc#6 $
// ------------------------------------------------------------------------------
// $Log: $
//
//========================================================================================
//.......1.........2.........3.........4.........5.........6.........7.........8.........9
//

#include "MerHashMap.hh"

char gseqbuf2[SEQ_BUFFER_SIZE2];

//extern "C++" {

//======================================================================================
//                                    MerSet
//=======================================================================================

//-------------------------------------------
// * ContainsFWDREV
//-------------------------------------------
bool
MerSet::ContainsFWDREV(Mer key)
{

  MerSet::iterator it;
  it = find(key);
  if (it != end()) return(true);

  // OK, so look for the reverse key. 
  Mer revkey(key);
  revkey.ReverseComplement();
  it = find(revkey);	 
  if (it != end()) return(true);

  return(false); 
}

//-------------------------------------------
// * ReadRepeatMers
//-------------------------------------------
//  Reads repeat mers in Paul's format, mer {ws} count. 
//
void 
MerSet::ReadRepeatMers(istream &fin,int minCount)
{
  string line;
  long merCount = 1,addCount = 0;
  do{
    fin.getline(readbuf,READ_BUFFER_SIZE);
    line = readbuf;
    int offset = 0;
    string mer = SplitNext(line,offset);
    int count = NextInt(line,offset);

    if (count >= minCount){
      insert(mer);
      addCount++;
    }
    if (!(merCount%500000)){
      cout<<merCount<<" mers processed, "<<addCount;
      cout<<" mers with count >="<<minCount<<" added to kill set."<<endl;
    }
    merCount++;

  }while(!fin.eof());
}

//---------------------------------------------
// * Add
//---------------------------------------------
// Adds the mer contents of the given stream to the MerSet
//
void
MerSet::Add(Sequence3Bit &Seq)
{
  bool bContainsNX;
  int maxBaseIdx = (Seq.mBaseSize - Mer::mMerSize+1);
  for(int baseIdx = 0;baseIdx< maxBaseIdx;baseIdx++){
    // Create a mer out of the next Mer::mMerSize bases starting at baseIdx
    // recording if N or X encountered. 
    Mer TempMer(Seq,baseIdx,bContainsNX);	 
    
    if (!bContainsNX){
      // Record mer...
      Mer2IntMap::iterator it;
      Add(TempMer);
    }
  }
}





//---------------------------------------------
// * Add
//---------------------------------------------
// Adds the mer contents of the given stream to the MerSet
//
void
MerSet::Add(istream &fin)
{
  Sequence3Bit tempSeq;

  long readIdx = 0;
  do{
    // KJD This is broken.  Fails to read the last one. 
    tempSeq.GetNextRead(fin,gseqbuf2,SEQ_BUFFER_SIZE2);	 
    Add(tempSeq);	 
  }while(!fin.eof());
}


//======================================================================================
//                                    MerHashMap
//=======================================================================================


//------------------------------------------
// * Add
//------------------------------------------
// Adds the mer contents of the given stream to the hashmap.  The stream must be in 
// multiple-FASTA format.  This version adds only the reads from read number Begin to read 
// number End.  Mers with N or X or lower case are skipped. 
//
// Differs from AddToHashMap by not saving the sequence for future reference. 
//  
// This version also adds only mers that begin with the sequence Subset
//
void 
Mer2IntMap::Add(istream &fin,unsigned int Subset)
{
  Sequence3Bit tempSeq;

  long readIdx = 0;
  do{
    tempSeq.GetNextReadTrimLowQual(fin,gseqbuf2,SEQ_BUFFER_SIZE2);
    Add(tempSeq,Subset);	 	 
  }while(!fin.eof());
}

//---------------------------------------------
// * Add
//---------------------------------------------
// Adds the mer contents of the given stream to the hashmap.  The stream must be in 
// multiple-FASTA format.  This version adds only the reads from read number Begin to read 
// number End.  Mers with N or X or lower case are skipped. 
//
// Differs from AddToHashMap by not saving the sequence for future reference. 
void
Mer2IntMap::Add(istream &fin)
{
  Sequence3Bit tempSeq;

  long readIdx = 0;
  do{
    // KJD This is broken.  Fails to read the last one. 
    tempSeq.GetNextRead(fin,gseqbuf2,SEQ_BUFFER_SIZE2);
		Add(tempSeq);	 

		

  }while(!fin.eof());
}


//---------------------------------
// * Add
//---------------------------------
// Adds a sequence, in 3-bit encoding, to the hash map, starting at SeqIdx.  That is, 
// the Mer::mMerSize bases starting at SeqIdx in Seq will be added to the hash.  Does nothing
// if the mer contains N or X. 
//
// KJD BUG:  Shouldn't this skip lower case also?   Does it??
//
void
Mer2IntMap::Add(Sequence3Bit &Seq,unsigned int Subset)
{
  bool bContainsNX;
  int maxBaseIdx = (Seq.mBaseSize - Mer::mMerSize+1);

  for(int baseIdx = 0;baseIdx< maxBaseIdx;baseIdx++){
    // Create a mer out of the next Mer::mMerSize bases starting at baseIdx
    // recording if N or X encountered. 
    Mer TempMer(Seq,baseIdx,bContainsNX);	 

    // Skip mers that are not part of the specified subset. 
    if (TempMer.InSubset(Subset)){		
      if (!bContainsNX){
	// Record mer...
	Mer2IntMap::iterator it;
	it = (*this).find(TempMer);
		  
	// If we already know this mer, add to it's count
	if (it != end()){
	  (*it).second++;
	  mTotalMersInHash++;  // Used to compute a global average. 
	}else{
	  // Otherwise add the mer to the map and set it's count to one. 
	  (*this)[TempMer] = 1;
	  mTotalMersInHash++;  // Used to compute a global average. 
	}
      }
    }
  }
}

 
//---------------------------------
// * Add
//---------------------------------
// Adds a sequence, in 3-bit encoding, to the hash map, starting at SeqIdx.  That is, 
// the Mer::mMerSize bases starting at SeqIdx in Seq will be added to the hash.  Does nothing
// if the mer contains N or X. 
//
// KJD BUG:  Shouldn't this skip lower case also?   Does it??
//
void
Mer2IntMap::Add(Sequence3Bit &Seq)
{
  bool bContainsNX;
  int maxBaseIdx = (Seq.mBaseSize - Mer::mMerSize+1);
  for(int baseIdx = 0;baseIdx< maxBaseIdx;baseIdx++){
    // Create a mer out of the next Mer::mMerSize bases starting at baseIdx
    // recording if N or X encountered. 
    Mer TempMer(Seq,baseIdx,bContainsNX);	 
	 
    if (!bContainsNX){
      // Record mer...
      Mer2IntMap::iterator it;
      it = (*this).find(TempMer);
		
      // If we already know this mer, add to it's count
      if (it != end()){
	(*it).second++;
	mTotalMersInHash++;  // Used to compute a global average. 
      }else{
	// Otherwise add the mer to the map and set it's count to one. 
	(*this)[TempMer] = 1;
	mTotalMersInHash++;  // Used to compute a global average. 
      }
    }
  }
}



//---------------------------------
// * BeginsWith
//---------------------------------
//
bool 
Mer2IntMap::BeginsWith(string &Subset,Sequence3Bit &Seq,int bIdx)
{
  for(int i = 0;i< Subset.size();i++){
    char seqChar = Seq.Bit2Base(Seq[i+bIdx]);
    char subChar = Subset[i];
	 
    if (tolower(seqChar) != tolower(subChar)){
      return(false);
    }
  }
  return(true);
}



//-------------------------------------------
// * Add
//-------------------------------------------
// Adds the mer contents of the given stream to the hashmap.  The stream must be in 
// multiple-FASTA format.  This version adds only the reads from read number Begin to read 
// number End.  Mers with N or X or lower case are skipped. 
//
// Differs from AddToHashMap by not saving the sequence for future reference. 
//  
// This version also adds only mers that begin with the sequence Subset
//
void
Mer2IntMap::Add(istream &fin,string Subset)
{
  Sequence3Bit tempSeq;

  long readIdx = 0;
  do{
    tempSeq.GetNextReadTrimLowQual(fin,gseqbuf2,SEQ_BUFFER_SIZE2);
    Add(tempSeq,Subset);
	 
    readIdx++;

    if (!(readIdx%20000)){
      cout<<readIdx<<" reads processed, size="<<size()<<" TotalMers="<<mTotalMersInHash<<endl;
    }
	 
  }while(!fin.eof());
}


//-------------------------------
// * Add
//-------------------------------
// Adds a sequence, in 3-bit encoding, to the hash map, starting at SeqIdx.  That is, 
// the Mer::mMerSize bases starting at SeqIdx in Seq will be added to the hash.  Does nothing
// if the mer contains N or X. 
//
// KJD BUG:  Shouldn't this skip lower case also?   Does it??
//
void
Mer2IntMap::Add(Sequence3Bit &Seq,string &Subset)
{
  bool bContainsNX;
  int maxBaseIdx = (Seq.mBaseSize - Mer::mMerSize);
  for(int baseIdx = 0;baseIdx< maxBaseIdx;baseIdx++){

    // Skip mers that do not begin with Subset
    if (BeginsWith(Subset,Seq,baseIdx)){
      // Create a mer out of the next Mer::mMerSize bases starting at baseIdx
      // recording if N or X encountered. 
      Mer TempMer(Seq,baseIdx,bContainsNX);	 
      
      if (!bContainsNX){
	// Record mer...
	Mer2IntMap::iterator it;
	it = (*this).find(TempMer);
	
	// If we already know this mer, add to it's count
	if (it != end()){
	  (*it).second++;
	  mTotalMersInHash++;  // Used to compute a global average. 
	}else{
	  // Otherwise add the mer to the map and set it's count to one. 
	  (*this)[TempMer] = 1;
	  mTotalMersInHash++;  // Used to compute a global average. 
	}
      }
    }
  }
}

//----------------------------------
// * Add
//----------------------------------
// Adds a sequence, in 3-bit encoding, to the hash map, starting at SeqIdx.  That is, 
// the Mer::mMerSize bases starting at SeqIdx in Seq will be added to the hash.  Does nothing
// if the mer contains N or X. 
//
// KJD BUG:  Shouldn't this skip lower case also?   Does it??
//
void
Mer2IntMap::Add(Mer M)
{
  // Record mer...
  Mer2IntMap::iterator it;
  it = (*this).find(M);
		
  // If we already know this mer, add to it's count
  if (it != end()){
    (*it).second++;
    mTotalMersInHash++;  // Used to compute a global average. 
  }else{
    // Otherwise add the mer to the map and set it's count to one. 
    (*this)[M] = 1;
    mTotalMersInHash++;  // Used to compute a global average. 
  }
}




